/////////////////////////////////////////////
// Unit:  Barrel Shifter
// Implementation Method: Pure Combinational Logic
// input ports:
//   dat_i:     the src operand to do shifting
//   shamt_i:   the number of bits to shift
//   shdir_i:   the direction to shift, 1'b1 indicates right shifting, while 1'b0 for the left
//   sharith_i: if set this bit, enable arithmatic shifting.Otherwise, arithmatic shifting is disable
// output ports:
//   dat_o:     the dst openrand which is the result after shifting
// parameters:
//   SHAMT_WIDTH: the width of shamt_i, which will be used to infer the width of shamt_i and dat_i
/////////////////////////////////////////////
//  Author:  Yao Li
//  Created: 2024.5.13
/////////////////////////////////////////////

module barrel_shifter #(
    parameter integer SHAMT_WIDTH = 5
) (
    input [2**SHAMT_WIDTH-1:0] dat_i,
    input [SHAMT_WIDTH-1:0]    shamt_i,
    input  shdir_i,
    input  sharith_i,
    output [2**SHAMT_WIDTH-1:0] dat_o
);

  localparam integer DataWidth = 2 ** SHAMT_WIDTH;

  wire [DataWidth-1:0] layer_din [0:SHAMT_WIDTH-1];
  wire [DataWidth-1:0] layer_dout[0:SHAMT_WIDTH-1];
  wire [          1:0] opsel     [0:SHAMT_WIDTH-1];

  assign layer_din[SHAMT_WIDTH-1] = dat_i;

  assign dat_o = layer_dout[0];

  genvar layeri;
  generate
    for (layeri = 0; layeri < SHAMT_WIDTH; layeri = layeri + 1) begin : gen_layer
      // decode operation to opsel for each layer
      assign opsel[layeri] = {
        (shdir_i & shamt_i[layeri]), ((~shdir_i | sharith_i) & shamt_i[layeri])
      };
      // mux on each layer
      layer_mux#(
          .LAYERI(layeri),
          .DATA_WIDTH(DataWidth)
      ) layer_inst(
          .layer_din(layer_din[layeri]),
          .opsel(opsel[layeri]),
          .layer_dout(layer_dout[layeri])
      );
      // connect each layer
      if (layeri < SHAMT_WIDTH - 1) begin : gen_layer_conn
        assign layer_din[layeri] = layer_dout[layeri+1];
      end
    end

  endgenerate

endmodule

